package com.biz.primus.ms.oms.service.split;

import com.biz.primus.base.exception.BizSilentException;
import com.biz.primus.common.utils.JsonUtils;
import com.biz.primus.common.utils.ValueUtils;
import com.biz.primus.model.oms.enums.OrderState;
import com.biz.primus.model.oms.exception.OmsExceptions;
import com.biz.primus.model.oms.vo.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.oms.dao.po.OmsConsignee;
import com.biz.primus.ms.oms.dao.po.OmsOrder;
import com.biz.primus.ms.oms.dao.po.OmsOrderItem;
import com.biz.primus.ms.oms.dao.repository.OmsOrderRepository;
import com.biz.primus.ms.oms.stream.OrderCollectionSource;
import com.biz.primus.ms.oms.trans.ConsigneeConverter;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author yanweijin
 * @date 2017/11/14
 */
@Service
@Slf4j
public class OrderSplitServiceImpl extends AbstractBaseService implements OrderSplitService {


	@Autowired
	private OmsOrderRepository omsOrderRepository;

	@Autowired
	private OrderCollectionSource orderDataSource;

	@Autowired
	private ConsigneeConverter consigneeConverter;

	/**
	 * 订单手动拆单:
	 *
	 * 1. 根据
	 *
	 *
	 *
	 * @param req
	 */
	@Override
	public void splitOrder(SplitRequestVo req) {
		if(StringUtils.isBlank(req.getOrderCode())){
			throw new BizSilentException(OmsExceptions.ORDER_CODE_ERROR);
		}
		OmsOrder order = omsOrderRepository.findByOrderCode(req.getOrderCode());
		List<OmsOrderVo> newOrders = transOrder(req, order);
		newOrders.stream().map(o->MessageBuilder.withPayload(o).build()).forEach(
			msg->delayer.executeAfterTransactionCommit(
				//事务提交后,将订单数据推出
				()-> orderDataSource.orderDataOutput().send(msg)
			)
		);
		omsOrderRepository.save(order);
	}

	/**
	 * 根据拆单请求构建拆出的订单,并在原订单中减去相应内容(商品数量,订单金额等)
	 * @param req
	 * @param order
	 * @return
	 */
	private List<OmsOrderVo> transOrder(SplitRequestVo req, OmsOrder order) {
		//将同一个门店的商品分类
		ImmutableListMultimap<String, SplitItemVo> multimap = Multimaps.index(req.getSplitItems(), SplitItemVo::getTargetDepotCode);

		return multimap.keys().stream().filter((code) -> Objects.equals(order.getDepotCode(), code)).map(multimap::get).map(splitedItems -> {
			//获取原订单的原始数据,包括pos号等内容
			OmsOrderVo orderVo = order.getOriginData();

			List<OmsItemVo> itemVos = splitItems(splitedItems, order.getItems());
			int splitCount = order.getSplitCount() + 1;
			//更新新订单的数据
			orderVo.setCode(order.getOrderCode() + "_" + splitCount);
			order.setSplitCount(splitCount);
			OmsConsignee consigneePo = order.getConsignee();
			orderVo.setConsignee(consigneeConverter.po2vo(consigneePo));
			orderVo.setRemark(order.getRemark());
			orderVo.setSelfRemark(order.getSelfRemark());
			orderVo.setOrderSource(order.getOrderSource());
			orderVo.setPaymentState(order.getPaymentState());
			orderVo.setState(OrderState.begin);

			orderVo.setItems(itemVos);
			//累加item中的金额
			itemVos.forEach(i -> {
				OmsOrderVo o = orderVo;
				accumulate(o::getDiscountAmount, o::setDiscountAmount, i.getDiscountAmount());
				accumulate(o::getUsePoint, o::setUsePoint, i.getUsePoint());
				accumulate(o::getAmount, o::setAmount, i.getFinalPrice() * i.getQuantity());
				accumulate(o::getDiscountCoupon, o::setDiscountCoupon, i.getDiscountCoupon());
				accumulate(o::getDiscountGrant, o::setDiscountGrant, i.getDiscountGrant());
				accumulate(o::getDiscountPop, o::setDiscountPop, i.getDiscountPop());
			});
			//在原始订单中减去分配给子订单的金额
			accumulate(order::getDiscountAmount, order::setDiscountAmount, -orderVo.getDiscountAmount());
			accumulate(order::getOrderAmount, order::setOrderAmount, -orderVo.getAmount());
			//拆单之后,运费不拆分
			orderVo.setFreightAmount(0);
			//分摊已支付金额
			Integer paidAmount = (int) (order.getPaidAmount() * (orderVo.getAmount().doubleValue() / order.getOrderAmount().doubleValue()));
			orderVo.setLiquidated(paidAmount);
			order.setPaidAmount(order.getPaidAmount() - paidAmount);
			orderVo.setSourceOrderCode(order.getOrderCode());
			orderVo.setSelfRemark(order.getSelfRemark() + "\n已经过人工拆单,原订单号为:" + order.getOrderCode() + "\n");
			return orderVo;
		}).collect(Collectors.toList());
	}

	private void accumulate(Supplier<Integer> attrGetter, Consumer<Integer> attrSetter, Integer value){
		Integer integer = ValueUtils.getValue(attrGetter.get());
		attrSetter.accept(integer + value);
	}



	private List<OmsItemVo> splitItems(List<SplitItemVo> sivList, List<OmsOrderItem> orderItems){
		ImmutableMap<Long, OmsOrderItem> map = Maps.uniqueIndex(orderItems, OmsOrderItem::getId);
		return sivList.stream().map(si->{
			OmsOrderItem i = map.get(si.getItemId());
			OmsItemVo vo = new OmsItemVo();
			vo.setBarcode(i.getBarcode());
			vo.setBrand(i.getBrandCode());
			vo.setCategory(i.getCategoryCode());
			try{
				List coupons = JsonUtils.json2Obj(i.getCouponOriginData(), List.class, CouponApportionmentVo.class);
				vo.setCoupons(coupons);
				List promotions = JsonUtils.json2Obj(i.getCouponOriginData(), List.class, PromotionApportionmentVo.class);
				vo.setPromotions(promotions);
			}catch (Exception e){
				log.warn("手动分单时json反序列化失败,优惠详情没有成功复制",e);
			}
			vo.setCode(i.getProductCode());
			vo.setName(i.getProductName());
			vo.setFinalPrice(i.getFinalPrice());
			vo.setMarketPrice(i.getMarketPrice());
			vo.setQuantity(si.getQuantity());
			vo.setWeight(i.getWeight());
			vo.setUsePoint(i.getUsePoint());
			ItemIntegerComputer computer = new ItemIntegerComputer(i,vo);
			vo.setUsePoint(computer.compute(i::getUsePoint,i::setUsePoint));
			vo.setDiscountAmount(computer.compute(i::getDiscountAmount,i::setDiscountAmount));
			vo.setDiscountPop(computer.compute(i::getDiscountAmount,i::setDiscountAmount));
			vo.setDiscountCoupon(computer.compute(i::getDiscountCoupon,i::setDiscountCoupon));
			vo.setDiscountGrant(computer.compute(i::getDiscountGrant,i::setDiscountGrant));
			computer.doSubQuantity();
			return vo;
		}).collect(Collectors.toList());
	}


	@AllArgsConstructor
	private class ItemIntegerComputer {
		private OmsOrderItem item;
		private OmsItemVo vo;

		/**
		 * 计算item中的integer分摊
		 * @param attrGetter
		 * @param attrSetter
		 * @return
		 */
		public Integer compute(Supplier<Integer> attrGetter, Consumer<Integer> attrSetter){
			double rate = vo.getQuantity().doubleValue()/item.getQuantity().doubleValue();
			Integer sourceInteger = attrGetter.get();
			Integer splitedInteger = (int)(sourceInteger * rate);
			attrSetter.accept(sourceInteger-splitedInteger);
			return splitedInteger;
		}

		/**
		 * 减去item数量
		 */
		public void doSubQuantity(){
			item.setQuantity(item.getQuantity()-vo.getQuantity());
		}
	}


}
